#include "process_supervision.h"

/* Registers the process supervisor as a supervisor of supervised */
int32_t register_supervisor(PCB* supervisor, int32_t supervised_pid){
  if(supervisor == NULL)
    return 0;
  PCB* supervised = get_process_by_pid(supervised_pid);
  if(supervised != NULL){
    //inserts supervisor as one of supervised's supervisors (and vice versa)
    if(supervision_queue_put(pcb_get_supervisors(supervised),supervisor)){
      if(!supervision_queue_put(pcb_get_supervised(supervisor),supervised)){
	//undo the first step
	supervision_queue_remove(pcb_get_supervisors(supervised),supervisor);
	return 0;
      }
      return 1;
    }
  }
  return 0;
}

/* goes through all processes supervised by supervisor and removes it from their supervisors */
int32_t unregister_supervisor(PCB* supervisor){
  if(supervisor == NULL)
    return 0;
  supervision_queue* supervised = pcb_get_supervised(supervisor);
  int i,k,count = supervised->count;
  for(i=0,k=0; i < MAX_SUPERVISION && k < count; i++){
    if(supervised->pcb[i] != NULL){
      //we found a supervised process
      k++;
      //supervisor is no longer a supervisor of this process
      if(!supervision_queue_remove(pcb_get_supervisors(supervised->pcb[i]),supervisor)){
	//something went wrong, we stop
	return 0;
      }
      // remove the supervised from supervisor's supervised queue*/
      supervised->pcb[i] = NULL;
      supervised->count --;
    }
  }
  return (k==count?1:0); //have all the processes been deleted or was the count wrong?
}

/* goes through all supervised's supervisors and unregisters it as one of their supervised process */
int32_t unregister_supervised(PCB* supervised){
  if(supervised == NULL)
    return 0;
  supervision_queue* supervisors = pcb_get_supervisors(supervised);
  int i,k,count = supervisors->count;
  for(i=0,k=0; i < MAX_SUPERVISION && k < count; i++){
    if(supervisors->pcb[i] != NULL){
      //we found a supervisor process
      k++;
      //supervised is no longer supervised by this process
      if(!supervision_queue_remove(pcb_get_supervised(supervisors->pcb[i]),supervised)){
	//something went wrong, we stop
	return 0;
      }
      // remove the supervisor from supervised's supervisors queue*/
      supervisors->pcb[i] = NULL;
      supervisors->count --;
    }
  }
  return (k==count?1:0); //have all the processes been deleted or was the count wrong?
}

/* signal terminated's termination to its supervisors */
int32_t signal_termination(PCB* terminated, int32_t code) {
  if(terminated == NULL)
    return 0;
  int32_t terminated_pid = pcb_get_pid(terminated);
  supervision_queue* supervisors = pcb_get_supervisors(terminated);
  /* signal all supervisors processes about termination */
  int i, k, count = supervisors->count;
  for (i=0, k=0; i < MAX_SUPERVISION && k < count; i++) {
    if (supervisors->pcb[i] != NULL ) {
      PCB* supervisor = supervisors->pcb[i];
      k++;
      /* build message */
      message m; 
      build_message(terminated_pid, pcb_get_pid(supervisor), code, SUPERVISION_MESSAGE, INT_DATA, &m);
      send_pcb(supervisor, m);
    }
  }
  //the supervisors have been notified, let's clean the place
  unregister_supervisor(terminated); //we no longer supervise anyone
  unregister_supervised(terminated); //we no longer are supervised by anyone
  return (k==count?1:0);
}

/*
void add_supervisor_to_supervisee(PCB* supervisee, PCB* supervisor) {
  int i = 0;
  int8_t not_inserted = 1; // I hope this works good as a bool.
  uint32_t supervisor_pid = pcb_get_pid(supervisor);
  while (not_inserted && i < MAX_SUPERVISION) {
    if (-1 == supervisee->supervisors.pid[i]) {
      supervisee->supervisors.pid[i] = supervisor_pid;
      supervisee->supervisors.count++;
      not_inserted = 0;
    }
    i++;
  }
}

void add_supervisee_to_supervisor(PCB* supervisee, PCB* supervisor) {
  int i = 0;
  int8_t not_inserted = 1; 
  uint32_t supervisee_pid = pcb_get_pid(supervisee);
  while (not_inserted && i < MAX_SUPERVISION) {
    if (-1 == supervisor->supervised.pid[i]) {
      supervisor->supervised.pid[i] = supervisee_pid;
      supervisor->supervised.count++;
      not_inserted = 0;
    }
    i++;
  }
}

void clean_supervisors(PCB* supervisee) {
  int i = 0;
  int32_t supervisee_pid = pcb_get_pid(supervisee);
  // go through all the supervised from the supervisor 
  for (i=0; i<MAX_SUPERVISION; i++) {
    // get supervised process.
    if (-1 != supervisee->supervisors.pid[i]) {
      PCB* supervisor = get_process_by_pid(supervisee->supervisors.pid[i]);
      int8_t not_deleted = 1; // I hope this works good as a bool.
      int pos = 0;
      while (not_deleted && pos < MAX_SUPERVISION) {
        if (supervisor->supervised.pid[pos] == supervisee_pid) {
          supervisor->supervised.pid[pos] = -1;
          supervisor->supervised.count--;
          not_deleted = 0;
        }
        pos++;
      }
      // delete supervisor from supervisee
      supervisee->supervisors.pid[i] = -1;
    }
  }
}

void register_supervisor(PCB* supervisor, PCB** supervised, int supervised_length) {
  int i = 0;
  for (i=0; i<supervised_length; i++) {
    add_supervisor_to_supervisee(supervised[i], supervisor);
    add_supervisee_to_supervisor(supervised[i], supervisor);
  }
}

void register_supervisor2(int32_t supervisor, PCB* supervised, int supervised_length) {
  PCB* supervisor_pcb = get_process_by_pid(supervisor);
  PCB* supervised_array[1];
  supervised_array[0] = supervised;
  register_supervisor(supervisor_pcb, supervised_array, 1);
}

void unregister_supervisor(PCB* supervisor) {
  int i = 0, j = 0;
  int32_t supervisor_pid = pcb_get_pid(supervisor);
  // go through all the supervised from the supervisor 
  for (i=0; i<MAX_SUPERVISION; i++) {
    // get supervised process.
    PCB* supervisee = get_process_by_pid(supervisor->supervised.pid[i]);
    // go through supervisors till supervisor is found, delete.
    int8_t not_deleted = 1; // I hope this works good as a bool.
    while (not_deleted && j < MAX_SUPERVISION) {
      if (supervisee->supervisors.pid[j] == supervisor_pid) {
        supervisee->supervisors.pid[j] = -1;
        supervisee->supervisors.count--;
        not_deleted = 0;
      }
      j++;
    }
    // delete supervisee from supervisor.
    supervisor->supervised.pid[i] = -1;
  }
}


void signal_termination(PCB* terminated, int code) {
  int32_t terminated_pid = pcb_get_pid(terminated);
  // signal all supervision processes about termination 
  int i = 0;
  for (i=0; i < MAX_SUPERVISION; i++) {
    int32_t supervisor_pid = terminated->supervisors.pid[i];
    if (supervisor_pid != -1) {
      // build message 
      message m; 
      int priority = 9, data_flag = INT_DATA;
      build_message(terminated_pid, supervisor_pid, code, priority, data_flag, &m);
      send(supervisor_pid, m);
      clean_supervisors(terminated);
    }
  }
}
*/
